#include "GlGame.h"

#include <time.h>
#include <windows.h>
#include "GlCamera.h"
#include "GlConfigFile.h"
#include "GlDxAudioManager.h"
#include "GlDxSpotLight.h"
#include "GlDxSprite.h"
#include "GlWindow.h"
#include "GlGameListener.h"
#include "GlSize.h"
#include "GlSpriteDescriptionManager.h"
#include "GlSpriteRenderer.h"
#include "GlTextureManager.h"
#include "Utility.h"

namespace GraphicLibrary
{
	//--------------------------------------
	Game::Game()
		: mWindow(0)
		, mExit(false)
		, mListener(0)
		, mDevice(0)
		, mImmediateContext(0)
		, mSwapChain(0)
		, mRenderTarget(0)
		, mBlendState(0)
	{}

	//--------------------------------------
	Game::~Game()
	{
		destroy();
	}

	//--------------------------------------
	void Game::run(GameListener *game)
	{
		mListener = game;
		init();

		long time = GetTickCount();
		mExit = false;
		FLOAT clearColor[4] = {
			0.0f,
			0.0f,
			0.0f,
			0.0f
		};
		mListener->init();

		while(!mExit)
		{
			mWindow->manage();
			long now = GetTickCount();
			float deltaTime = (now - time) / 1000.0f;
			time = now;

			SpriteRenderer::GetSingleton()->tick(deltaTime);
			if(mListener)
				mExit = mExit || mListener->tick(deltaTime);

			mImmediateContext->ClearRenderTargetView(mRenderTarget, clearColor);
			SpriteRenderer::GetSingleton()->renderAll(mImmediateContext);
			mSwapChain->Present(0, 0);
		}

		destroy();
	}

	//--------------------------------------
	void Game::init()
	{
		srand(time(0));

		ConfigFile::Init(Utility::getResource("SneakingMission.ini"));
		Window::Init();
		UINT width = ConfigFile::GetSingleton()->get("Graphic").get<UINT>("Window/Width");
		UINT height = ConfigFile::GetSingleton()->get("Graphic").get<UINT>("Window/Height");
		UINT left = ConfigFile::GetSingleton()->get("Graphic").get<UINT>("Window/InitPos/Left");
		UINT top = ConfigFile::GetSingleton()->get("Graphic").get<UINT>("Window/InitPos/Top");
		const char *title = ConfigFile::GetSingleton()->get("Graphic").get<const char*>("Window/Title");

		mWindow = new Window(width, height, top, left, title);
		mWindow->connect(this);

		DXGI_SWAP_CHAIN_DESC scDesc;
		ZeroMemory(&scDesc, sizeof(DXGI_SWAP_CHAIN_DESC));
		scDesc.BufferDesc.Width = mWindow->getClientAreaWidth();
		scDesc.BufferDesc.Height = mWindow->getClientAreaHeight();
		scDesc.BufferDesc.RefreshRate.Numerator = 60;
		scDesc.BufferDesc.RefreshRate.Denominator = 1;
		scDesc.BufferDesc.Scaling = DXGI_MODE_SCALING_STRETCHED;
		scDesc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
		scDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;

		scDesc.SampleDesc.Count = 1;
		scDesc.SampleDesc.Quality = 0;

		scDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
		scDesc.BufferCount = 1;
		scDesc.OutputWindow = mWindow->getHandler();
		scDesc.Windowed = true;
		scDesc.SwapEffect = DXGI_SWAP_EFFECT_SEQUENTIAL;
		scDesc.Flags = 0;

		D3D_FEATURE_LEVEL flvl[] = {
			D3D_FEATURE_LEVEL_9_3
		};

		int cdFlags = D3D11_CREATE_DEVICE_SINGLETHREADED;
#ifdef _DEBUG
		cdFlags |= D3D11_CREATE_DEVICE_DEBUG;
#endif
		HRESULT res = D3D11CreateDeviceAndSwapChain(0, D3D_DRIVER_TYPE_HARDWARE, 0,cdFlags , flvl, 1, D3D11_SDK_VERSION, &scDesc, &mSwapChain,  &mDevice, &mFeatureLvl, &mImmediateContext);

		if(res != S_OK)
			throw ElDxInitializationError;

		ID3D11Texture2D *backBuffer = 0;
		res = mSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), reinterpret_cast<void**>(&backBuffer));
		if(res != S_OK)
			throw ElDxInitializationError;
		res = mDevice->CreateRenderTargetView(backBuffer, 0, &mRenderTarget);
		backBuffer->Release();
		if(res != S_OK)
			throw ElDxInitializationError;

		D3D11_VIEWPORT vp;
		ZeroMemory(&vp, sizeof(D3D11_VIEWPORT));
		vp.Height = (FLOAT)mWindow->getClientAreaHeight();
		vp.Width = (FLOAT)mWindow->getClientAreaWidth();
		vp.MinDepth = 0.0f;
		vp.MaxDepth = 1.0f;
		vp.TopLeftX = 0.0f;
		vp.TopLeftY = 0.0f;
		mImmediateContext->RSSetViewports(1, &vp);
		mImmediateContext->OMSetRenderTargets(1, &mRenderTarget, 0);

		D3D11_BLEND_DESC blendDesc;
		blendDesc.AlphaToCoverageEnable = false;
		blendDesc.IndependentBlendEnable = false;
		blendDesc.RenderTarget[0].BlendEnable = true;
		blendDesc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
		blendDesc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
		blendDesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
		blendDesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
		blendDesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;
		blendDesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
		blendDesc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;

		res = mDevice->CreateBlendState(&blendDesc, &mBlendState);
		if(res != S_OK)
			throw ElDxInitializationError;
		mImmediateContext->OMSetBlendState(mBlendState, 0, 0xffffffff);

		TextureManager::Init(mDevice);
		Size screenSize = Size(static_cast<float>(mWindow->getClientAreaWidth()), static_cast<float>(mWindow->getClientAreaHeight()));
		SpriteRenderer::Init(mDevice, screenSize);
		Camera::Init(mWindow);
		DxSprite::Init(mDevice);
		DxSpotLight::Init(mDevice);
		SpriteDescriptionManager::GetSingleton()->addDescription(Utility::getResource("Sprites.xml"));

		AudioLibrary::AudioManager::Init(new AudioLibrary::DxAudioManager(mWindow->getHandler()));
	}

	//--------------------------------------
	void Game::destroy()
	{
		delete mWindow;
		mWindow = 0;

		if(mSwapChain)
		{
			mSwapChain->Release();
			mSwapChain = 0;
		}

		if(mBlendState)
		{
			mBlendState->Release();
			mBlendState = 0;
		}

		if(mRenderTarget)
		{
			mRenderTarget->Release();
			mRenderTarget = 0;
		}
		if(mImmediateContext)
		{
			mImmediateContext->Release();
			mImmediateContext = 0;
		}
		if(mDevice)
		{
			mDevice->Release();
			mDevice = 0;
		}
	}

	//--------------------------------------
	void Game::afterWindowClosed(const MyDistributor *caller)
	{
		mExit = true;
	}

	//--------------------------------------
	void Game::afterMouseDown(const MyDistributor *caller, const ButtonId button, const float x, const float y)
	{
		if(mListener && button == BiLeft)
			mListener->mouseDown(x, y);
	}

	//--------------------------------------
	void Game::afterMouseUp(const MyDistributor *caller, const ButtonId button, const float x, const float y)
	{
		if(mListener && button == BiLeft)
			mListener->mouseUp(x, y);
	}

	//--------------------------------------
	void Game::afterMouseMove(const MyDistributor *caller, const float x, const float y)
	{
		if(mListener)
			mListener->mouseMove(x, y);
	}

	//--------------------------------------
	void Game::afterKeyDown(const MyDistributor *caller, const unsigned int key)
	{
		if(mListener)
			mListener->keyPressed(key);
	}

	//--------------------------------------
	void Game::afterKeyUp(const MyDistributor *caller, const unsigned int key)
	{
	}
}
